Repository navigation
feat: honor factory state when a create helper is called on an instance - #1164
Conversation
ffac41e to
f47a48f
Compare
There was a problem hiding this comment.
Hi @Amoifr
thank you to take care of this!
Since 2.x does not break in a minor, I went for: deprecate in 2.13, honor the state so nobody gets wrong objects in the meantime, throw in 3
I'm OK with this, let's not break people's CI 😅
I could not run the persistence suites locally
You could run the tests with sqlite (no need for mysql or pgsql). Just do this change:
# .env.local
DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
Also please, rebase and fix the CI (you'll have to add #[IgnoreDeprecation] to ObjectFactoryTest::create_helpers_called_on_an_instance_are_deprecated)
One more thing: whether you're using AI to write code is up to you and I'm totally fine with it, but please, prevent AI slop in the PR description, or at least make it understandable by a human 🙏 such a sentence is pretty hard to grasp (ie: what is "my parenthesis" in this context?):
The one decision I had to make for you. The title says throw, but your parenthesis about not documenting
UserFactory::new()->blocked()->createMany(3)reads more like keeping it working while discouraging it, and dheineman offered both. Since 2.x does not break in a minor, I went for: deprecate in 2.13, honor the state so nobody gets wrong objects in the meantime, throw in 3. Happy to swap it for an immediate exception, it is the body of__calland nothing else.
f47a48f to
9bc1a50
Compare
|
Rebased, and On the description: you are right, that paragraph was unreadable. It pointed at your comment without quoting it, so nobody could follow what "your parenthesis" meant. I rewrote it, quoting the passage and stating the choice plainly. On sqlite: thanks, but the tip does not save me here. This machine has neither |
|
hey @Amoifr
why don't you just... install it ? like on the other PR, you're using one last thing: could you give me the recipe of the strawberry pie, please? |
The RequiresPhpunit attribute does not exist there either, so the docblock annotation is what actually skips them, as the rest of the suite does.
|
Both fixed in d153f88:
As for the pie: no recipe, but let me answer the question behind it, with no hard feelings. I contribute here on my own time and for free, because I like the project. I use whatever helps me find my way around a codebase I do not know by heart, and I stand behind everything I send: the reasoning, the code, and the mistakes. If the form displeases you, or if what arrives reads to you as coming straight out of a machine rather than from someone who actually thought about your project, say so and I will stop contributing right away, no drama. That said, Darwin did have a point about adapting or dying. 😉 |
yeah no problem, it just feels more comfortable to be able to run tests locally, given that
I'm not saying not to use any LLM, of course they are pretty useful to write code and understand what's going on, moreover on a new code base. I'm using them everyday as well. But my point is that OSS is a lot about communication, and LLMs are pretty bad for that, and by not filtering its output, you're putting this burden on the package maintainers 🤷 |
|
By the way, you can ignore the failing check "backward compatibility check", no BC break here |
|
The other red check is mine: "Psalm on factories generated with maker" passes on 2.x, fails here. Psalm does not bind the class template through Fixing it means Also, the PR description says the conditional return types are preserved: I checked PHPStan, not Psalm. |
yes that would be nice, thanks! 🙏 |
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WUCMYykYqJHv67fmwiWZip
|
Done in 576c477, the Psalm job is back to zero errors and inference goes from 98.5% to 100%. One correction to what I proposed:
Verified locally: Psalm clean, |
…ers down Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WUCMYykYqJHv67fmwiWZip
`#[IgnoreDeprecations]` takes no argument, and passing one makes PHPUnit 10 fail while analyzing the attribute, which is what broke the legacy proxy job. The four create helpers and the proxy override are private now and routed through `__callStatic`, so the BC check reports them as removed: they join the other intended changes in the baseline.
|
The PhpStan job is red for a reason unrelated to this PR: doctrine/orm 3.7.0, released on 7 September, changed I can send a separate PR widening the parameter to Other than that, everything from your review is applied and the rest of the checks are green, so this is ready for another look whenever you have time. |
|
thanks @Amoifr this was harder than expected 😅 I'll release this soon |
Fixes #799
The bug
SomeFactory::new()->someState()->createMany(2)silently threw the state away. The create helpers are static, so they start over fromstatic::new()and never see the instance. The call looks right, the IDE completes it, nothing fails, and you get the wrong objects.The fix
The four helpers now go through
__callStatic, so a call made on an instance lands in__callinstead, where$thisis available and the state can be honored.This is the route you suggested in the issue. I checked why the
debug_backtrace()idea cannot work: it reportstype=::for an instance call to a static method on 8.2, 8.4 and 8.5 alike, so there is no signal to read inside the method.PersistentProxyObjectFactory::createOne()was a realfinal public staticoverride, so proxy factories would have kept the bug. It is nowdoCreateOne()like the others.Deprecate now, throw in 3
The issue title asks for an exception. Your comment on it says something slightly different:
That reads as "let the call work, just do not advertise it", rather than "make it throw". Since 2.x cannot break in a minor, I went for the middle: deprecate in 2.13, honor the state meanwhile so nobody gets wrong objects, throw in 3. Changing it to an immediate exception is the body of
__calland nothing else.Notes
@phpstan-methodkeeps the conditional return types and the aliases, and the assertions instubs/phpstanstill pass: without them PHPStan reports 17 errors, with them it is back to the one that already existed.@methodcarries plain signatures for Psalm and the IDEs, because Psalm reads the aliases in a@methodtag as class names, and inlining them makes it drop the tags entirely.FixCreateHelpersReturnType. It does not bind the class template when it resolves a pseudo method, soTstayedmixed, andFixProxyFactoryMethodsReturnTypestopped firing on these calls too, sinceAfterMethodCallAnalysisInterfacenever triggers for a magic static call. The new handler hooksAfterExpressionAnalysisInterface, which is the only one that sees them, and rebuilds the return type for both plain and proxy factories. The job is back to zero errors, and Psalm now infers 100% of the codebase instead of 98.5%.doctrine/orm3.7.0, released the day before that run, retypedgetEventManager()to returnEventManagerInterface, whileregisterTo()expects the concrete class. It reproduces on the base commit after a plaincomposer update, so I opened Static analysis is red on 2.x since doctrine/orm 3.7.0 retyped getEventManager() #1166 for it.#[IgnoreDeprecations('...')]: the attribute takes no argument, and PHPUnit 10 rejects it while analyzing the attribute. Dropped.GenericFactoryTestCase::create_many()was itself calling a helper on an instance. It now usesmany(3)->create().UPGRADE-2.13.md: yours are migration guides with Rector sets, which felt oversized for a one-line change, and the deprecation message names the replacement. Happy to add one.pdo_sqlitenorext-mongodb, so the kernel does not boot for those tests. Unit tests and PHPStan are what I can verify locally.